package com.starleague.battletech.unit.mech.jpa.appengine;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.starleague.battletech.BattletechException;
import com.starleague.battletech.game.IBattletechGame;
import com.starleague.battletech.game.IBattletechTeam;
import com.starleague.battletech.game.jpa.appengine.BattletechGameGae;
import com.starleague.battletech.game.jpa.appengine.BattletechTeamGae;
import com.starleague.battletech.unit.BattletechUnitMovementType;
import com.starleague.battletech.unit.IPilot;
import com.starleague.battletech.unit.IWeight;
import com.starleague.battletech.unit.jpa.appengine.PilotGae;
import com.starleague.battletech.unit.mech.AbstractMechFactory;
import com.starleague.battletech.unit.mech.IMechActuator;
import com.starleague.battletech.unit.mech.IMechHitLocation;
import com.starleague.battletech.unit.mech.IMechHitLocationHead;
import com.starleague.battletech.unit.mech.IMutableMech;
import com.starleague.battletech.unit.mech.MechActuatorType;
import com.starleague.battletech.unit.mech.MechHitLocationPosition;

@Singleton
@Named
public class MechGaeFactory
        extends AbstractMechFactory<MechGae, MechGae>
{
    @Inject
    public MechGaeFactory()
    {
        super(new MechInternalStructureGaeFactory(), new MechEngineGaeFactory(), new MechGyroGaeFactory());
    }

    @Override
    protected MechGae createBipedMech(IBattletechGame game,
                                      IBattletechTeam team,
                                      IPilot pilot,
                                      IWeight weight)
    {
        MechGae mech = new MechGae((BattletechGameGae) game,
                                   (BattletechTeamGae) team,
                                   (PilotGae) pilot,
                                   weight,
                                   BattletechUnitMovementType.BIPED);

        if (game != null)
            ((BattletechGameGae) game).getMechs().add(mech);

        return mech;
    }

    @Override
    protected MechGae createQuadrupedMech(IBattletechGame game,
                                          IBattletechTeam team,
                                          IPilot pilot,
                                          IWeight weight)
    {
        MechGae mech = new MechGae((BattletechGameGae) game,
                                   (BattletechTeamGae) team,
                                   (PilotGae) pilot,
                                   weight,
                                   BattletechUnitMovementType.QUADRUPED);

        if (game != null)
            ((BattletechGameGae) game).getMechs().add(mech);

        return mech;
    }

    @Override
    protected MechHitLocationGae addLeg(IMutableMech mech,
                                        MechHitLocationPosition position,
                                        int internalStructurePoints)
            throws BattletechException
    {
        return addHitLocation(mech, position, internalStructurePoints);
    }

    @Override
    protected MechHitLocationGae addArm(MechGae mech,
                                        MechHitLocationPosition position,
                                        int internalStructurePoints)
            throws BattletechException
    {
        return addHitLocation(mech, position, internalStructurePoints);
    }

    @Override
    protected MechHitLocationGae addHead(IMutableMech mech,
                                         int internalStructurePoints)
            throws BattletechException
    {
        return addHitLocation(mech, MechHitLocationPosition.HEAD, internalStructurePoints);
    }

    @Override
    protected MechHitLocationGae addCenterTorso(IMutableMech mech,
                                                int internalStructurePoints)
            throws BattletechException
    {
        return addHitLocation(mech, MechHitLocationPosition.CENTER_TORSO, internalStructurePoints);
    }

    @Override
    protected MechHitLocationGae addSideTorso(IMutableMech mech,
                                              MechHitLocationPosition position,
                                              int internalStructurePoints)
            throws BattletechException
    {
        return addHitLocation(mech, position, internalStructurePoints);
    }

    @Override
    protected MechCoreComponentGae addCockpit(IMechHitLocationHead head,
                                              int... slotIndices)
            throws BattletechException
    {
        return addCoreComponent(head, MechCoreComponentGae.Type.COCKPIT, slotIndices);
    }

    @Override
    protected MechCoreComponentGae addSensors(IMechHitLocationHead head,
                                              int... slotIndices)
            throws BattletechException
    {
        return addCoreComponent(head, MechCoreComponentGae.Type.SENSORS, slotIndices);
    }

    @Override
    protected MechCoreComponentGae addLifeSupport(IMechHitLocationHead head,
                                                  int... slotIndices)
            throws BattletechException
    {
        return addCoreComponent(head, MechCoreComponentGae.Type.LIFE_SUPPORT, slotIndices);
    }

    @Override
    protected IMechActuator addActuator(IMechHitLocation hitLocation,
                                        MechActuatorType actuatorType,
                                        int criticalSlotIndex)
            throws BattletechException
    {
        MechCoreComponentGae actuator = new MechCoreComponentGae((MechHitLocationGae) hitLocation, actuatorType);
        addCoreComponentCriticalSlots((MechHitLocationGae) hitLocation, actuator, criticalSlotIndex);
        ((MechHitLocationGae) hitLocation).getCoreComponents().add(actuator);
        return actuator;
    }

    private MechHitLocationGae addHitLocation(IMutableMech mech,
                                              MechHitLocationPosition position,
                                              int internalStructurePoints)
    {
        MechHitLocationGae hitLocation = new MechHitLocationGae((MechGae) mech, position, 0, internalStructurePoints);
        ((MechGae) mech).getHitLocations().add(hitLocation);
        return hitLocation;
    }

    private MechCoreComponentGae addCoreComponent(IMechHitLocationHead head,
                                                  MechCoreComponentGae.Type coreComponentType,
                                                  int... slotIndices)
            throws BattletechException
    {
        MechCoreComponentGae coreComponent = new MechCoreComponentGae((MechHitLocationGae) head, coreComponentType);
        addCoreComponentCriticalSlots((MechHitLocationGae) head, coreComponent, slotIndices);
        ((MechHitLocationGae) head).getCoreComponents().add(coreComponent);
        return coreComponent;
    }

    // TODO: Move the hitLocation.addCoreComponentCriticalSlots() inside the CoreComponent class??
    private MechCoreComponentGae addCoreComponentCriticalSlots(MechHitLocationGae hitLocation,
                                                               MechCoreComponentGae coreComponent,
                                                               int... criticalSlotIndices)
            throws BattletechException
    {
        MechCriticalSlotGae[] positionCriticalSlots = hitLocation.getCriticalSlots();

        for (int criticalSlotIndex : criticalSlotIndices)
        {
            if (positionCriticalSlots[criticalSlotIndex] != null)
                throw new BattletechException(hitLocation.getPosition()
                                              + " should have a free slot at ["
                                              + criticalSlotIndex
                                              + "]: "
                                              + positionCriticalSlots[criticalSlotIndex]);

            coreComponent.getCriticalSlots().add(new MechCoreComponentCriticalSlotGae(coreComponent,
                                                                                      hitLocation.getPosition(),
                                                                                      criticalSlotIndex));
        }
        return coreComponent;
    }
}
